﻿namespace BathroomLocator.Services.Controllers
{
	using System;
	using System.Collections.Generic;
	using System.Globalization;
	using System.Linq;
	using System.Text;
	using System.Web.Mvc;
	using System.Web.Security;
	using BathroomLocator.Services.Infrastructure;
	using BathroomLocator.Services.MembershipWrappers;
	using BathroomLocator.Services.Models;
	using Microsoft.WindowsAzure;
	using Microsoft.WindowsAzure.StorageClient;
	using WindowsPhone.Recipes.Push.Messasges;

	public class AdminController : Controller
	{
		private readonly CloudTableClient cloudTableClient;
		private readonly CloudQueueClient cloudQueueClient;
		private readonly RoleProvider roleProvider;
		private readonly IMembershipService membershipService;
		private readonly IPushUserEndpointsRepository pushUserEndpointsRepository;

		public AdminController()
			: this(null, null, new RoleProviderWrapper(), new AccountMembershipService(), new PushUserEndpointContext())
		{
		}

		public AdminController(CloudTableClient cloudTableClient, CloudQueueClient cloudQueueClient, RoleProvider roleProvider, IMembershipService membershipService, IPushUserEndpointsRepository pushUserEndpointsRepository)
		{
			if (GetStorageAccountFromConfigurationSetting() == null)
			{
				if (cloudTableClient == null)
				{
					throw new ArgumentNullException("cloudTableClient", "Cloud Table Client cannot be null if no configuration is loaded.");
				}

				if (cloudQueueClient == null)
				{
					throw new ArgumentNullException("cloudQueueClient", "Cloud Queue Client cannot be null if no configuration is loaded.");
				}
			}

			this.cloudTableClient = cloudTableClient ?? GetStorageAccountFromConfigurationSetting().CreateCloudTableClient();
			this.cloudQueueClient = cloudQueueClient ?? GetStorageAccountFromConfigurationSetting().CreateCloudQueueClient();
			this.roleProvider = roleProvider;
			this.membershipService = membershipService;
			this.pushUserEndpointsRepository = pushUserEndpointsRepository;
		}

		[CustomAuthorize(Roles = RoleConstants.AdminRole)]
		public ActionResult Index()
		{
			return this.View();
		}

		[CustomAuthorize(Roles = RoleConstants.AdminRole)]
		public ActionResult UserPermissions()
		{
			var permissions = new List<UserPermissions>();

			foreach (MembershipUser user in this.membershipService.GetAllUsers())
			{
				var useTable = this.roleProvider.IsUserInRole(user.UserName, RoleConstants.TablesUsageRole);
				var useBlobs = this.roleProvider.IsUserInRole(user.UserName, RoleConstants.BlobsUsageRole);

				permissions.Add(new UserPermissions()
				{
					User = user.UserName,
					TablesUsage = useTable,
					BlobsUsage = useBlobs
				});
			}

			this.ViewData.Model = permissions;

			return this.View();
		}

		[CustomAuthorize(Roles = RoleConstants.AdminRole)]
		public ActionResult TablePermissions()
		{
			var permissions = new List<TablePermissions>();
			var tables = this.cloudTableClient.ListTables();
			foreach (var table in tables)
			{
				if (!table.Equals("secMembership", StringComparison.OrdinalIgnoreCase) &&
					!table.Equals("secRole", StringComparison.OrdinalIgnoreCase) &&
					!table.Equals(PushUserEndpointContext.PushUserTableName, StringComparison.OrdinalIgnoreCase))
				{
					var accessRole = table + RoleConstants.TableRoleSuffix;
					var publicRole = table + RoleConstants.PublicTableRoleSuffix;
					var isPublic = this.roleProvider.RoleExists(publicRole);
					var users = this.roleProvider.RoleExists(accessRole) ? this.roleProvider.GetUsersInRole(accessRole) : new string[0];

					permissions.Add(new TablePermissions
					{
						Table = table,
						IsPublic = isPublic,
						Users = users
					});
				}
			}

			var usersList = new List<string>();
			foreach (MembershipUser user in this.membershipService.GetAllUsers())
			{
				usersList.Add(user.UserName);
			}

			this.ViewData["users"] = usersList;
			this.ViewData.Model = permissions;

			return this.View();
		}

		[CustomAuthorize(Roles = RoleConstants.AdminRole)]
		public ActionResult UserPushNotifications()
		{
			this.ViewData.Model = this.pushUserEndpointsRepository.GetAllUsers();
			return this.View();
		}

		[CustomAuthorize(Roles = RoleConstants.AdminRole)]
		[HttpGet]
		public void SetUserPermissions(string user, bool useTables, bool useBlobs)
		{
			string[] users = { user };

			string[] tableRoles = { RoleConstants.TablesUsageRole };
			string[] blobsRoles = { RoleConstants.BlobsUsageRole };

			if (useTables)
			{
				this.roleProvider.AddUsersToRoles(users, tableRoles);
			}
			else if (this.roleProvider.IsUserInRole(user, RoleConstants.TablesUsageRole))
			{
				this.roleProvider.RemoveUsersFromRoles(users, tableRoles);
			}

			if (useBlobs)
			{
				this.roleProvider.AddUsersToRoles(users, blobsRoles);
			}
			else if (this.roleProvider.IsUserInRole(user, RoleConstants.BlobsUsageRole))
			{
				this.roleProvider.RemoveUsersFromRoles(users, blobsRoles);
			}
		}

		[CustomAuthorize(Roles = RoleConstants.AdminRole)]
		[HttpGet]
		public void AddTablePermission(string table, string user)
		{
			var tableRole = string.Format(CultureInfo.InvariantCulture, "{0}{1}", table, RoleConstants.TableRoleSuffix);
			if (!this.roleProvider.RoleExists(tableRole))
			{
				this.roleProvider.CreateRole(tableRole);
			}

			string[] users = { user };
			string[] roles = { tableRole };

			this.roleProvider.AddUsersToRoles(users, roles);
		}

		[CustomAuthorize(Roles = RoleConstants.AdminRole)]
		[HttpGet]
		public void RemoveTablePermission(string table, string user)
		{
			var tableRole = string.Format(CultureInfo.InvariantCulture, "{0}{1}", table, RoleConstants.TableRoleSuffix);
			if (this.roleProvider.RoleExists(tableRole) && this.roleProvider.IsUserInRole(user, tableRole))
			{
				string[] users = { user };
				string[] roles = { tableRole };
				this.roleProvider.RemoveUsersFromRoles(users, roles);
			}
		}

		[CustomAuthorize(Roles = RoleConstants.AdminRole)]
		[HttpGet]
		public void SetTablePublic(string table, bool isPublic)
		{
			var tableRole = string.Format(CultureInfo.InvariantCulture, "{0}{1}", table, RoleConstants.PublicTableRoleSuffix);
			if (isPublic)
			{
				if (!this.roleProvider.RoleExists(tableRole))
				{
					this.roleProvider.CreateRole(tableRole);
				}
			}
			else
			{
				if (this.roleProvider.RoleExists(tableRole))
				{
					this.roleProvider.DeleteRole(tableRole, true);
				}
			}
		}

		[CustomAuthorize(Roles = RoleConstants.AdminRole)]
		[HttpGet()]
		public ActionResult SendToastNotification(string userName, string message)
		{
			var resultList = new List<MessageSendResultLight>();
			var uris = this.pushUserEndpointsRepository.GetUsersByName(userName).Select(u => u.ChannelUri);
			var toast = new ToastPushNotificationMessage
				{
					SendPriority = MessageSendPriority.High,
					Title = message
				};

			foreach (var uri in uris)
			{
				var messageResult = toast.SendAndHandleErrors(new Uri(uri));
				resultList.Add(messageResult);
				if (messageResult.Status.Equals(MessageSendResultLight.Success))
				{
					this.QueueMessage(message, userName, new Uri(uri));
				}
			}

			return this.Json(resultList, JsonRequestBehavior.AllowGet);
		}

		[CustomAuthorize(Roles = RoleConstants.AdminRole)]
		[HttpGet]
		public ActionResult SendTileNotification(string userName, string message)
		{
			var resultList = new List<MessageSendResultLight>();
			var pushUserEndpointList = this.pushUserEndpointsRepository.GetUsersByName(userName);

			foreach (var pushUserEndpoint in pushUserEndpointList)
			{
				var tile = new TilePushNotificationMessage
				{
					SendPriority = MessageSendPriority.High,
					Count = ++pushUserEndpoint.TileCount
				};

				var messageResult = tile.SendAndHandleErrors(new Uri(pushUserEndpoint.ChannelUri));
				resultList.Add(messageResult);
				if (messageResult.Status.Equals(MessageSendResultLight.Success))
				{
					this.QueueMessage(message, userName, new Uri(pushUserEndpoint.ChannelUri));

					this.pushUserEndpointsRepository.UpdateUserEndpoint(pushUserEndpoint);
				}
			}

			return this.Json(resultList, JsonRequestBehavior.AllowGet);
		}

		[CustomAuthorize(Roles = RoleConstants.AdminRole)]
		[HttpGet]
		public ActionResult SendRawNotification(string userName, string message)
		{
			var resultList = new List<MessageSendResultLight>();
			var uris = this.pushUserEndpointsRepository.GetUsersByName(userName).Select(u => u.ChannelUri);
			var raw = new RawPushNotificationMessage
			{
				SendPriority = MessageSendPriority.High,
				RawData = Encoding.Default.GetBytes(message)
			};

			foreach (var uri in uris)
			{
				resultList.Add(raw.SendAndHandleErrors(new Uri(uri)));
			}

			return this.Json(resultList, JsonRequestBehavior.AllowGet);
		}

		private static CloudStorageAccount GetStorageAccountFromConfigurationSetting()
		{
			CloudStorageAccount account = null;

			try
			{
				account = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
			}
			catch (InvalidOperationException)
			{
				account = null;
			}

			return account;
		}

		private void QueueMessage(string message, string userName, Uri uri)
		{
			var queueName = string.Format(CultureInfo.InvariantCulture, "notification{0}", uri.GetHashCode());
			var queue = this.cloudQueueClient.GetQueueReference(queueName);
			queue.CreateIfNotExist();
			queue.AddMessage(new CloudQueueMessage(message));
		}
	}
}
